/* @file u12-motor.c
 * @brief all functions for motor control
 *
 * based on sources acquired from Plustek Inc.
 * Copyright (C) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
 *
 * History:
 * - 0.01 - initial version
 * - 0.02 - no changes
 * .
 * <hr>
 * This file is part of the SANE package.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 * As a special exception, the authors of SANE give permission for
 * additional uses of the libraries contained in this release of SANE.
 *
 * The exception is that, if you link a SANE library with other files
 * to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License.  Your use of that executable is in no way restricted on
 * account of linking the SANE library code into it.
 *
 * This exception does not, however, invalidate any other reasons why
 * the executable file might be covered by the GNU General Public
 * License.
 *
 * If you submit changes to SANE to the maintainers to be included in
 * a subsequent release, you agree by submitting the changes that
 * those changes may be distributed with this exception intact.
 *
 * If you write modifications of your own for SANE, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 * <hr>
 */

/*************************** some definitons *********************************/

#define _BACKSTEPS     120
#define _FORWARDSTEPS  120

/**************************** local vars *************************************/

static TimerDef u12motor_Timer;


/*************************** local functions *********************************/

/**
 */
static void
u12motor_DownloadNullScanStates(U12_Device * dev)
{
	memset(dev->scanStates, 0, _SCANSTATE_BYTES);
	u12io_DownloadScanStates(dev);
}

/**
 */
static void
u12motor_Force16Steps(U12_Device * dev, int dir)
{
	u_long dw;

	if (dir == _DIR_FW)
		u12io_DataToRegister(dev, REG_MOTOR0CONTROL, _FORWARD_MOTOR);
	else if (dir == _DIR_BW)
		u12io_DataToRegister(dev, REG_MOTOR0CONTROL, _BACKWARD_MOTOR);

	for (dw = 16; dw; dw--) {
		u12io_RegisterToScanner(dev, REG_FORCESTEP);
		_DODELAY(10);
	}
}

/**
 */
static void
u12motor_ModuleFreeRun(U12_Device * dev, u_long steps)
{
	SANE_Byte rb[6];

	rb[0] = REG_MOTORFREERUNCOUNT1;
	rb[1] = _HIBYTE(steps);
	rb[2] = REG_MOTORFREERUNCOUNT0;
	rb[3] = _LOBYTE(steps);
	rb[4] = REG_MOTORFREERUNTRIGGER;
	rb[5] = 0;

	u12io_DataToRegs(dev, rb, 3);
}

/**
 */
static SANE_Status
u12motor_PositionYProc(U12_Device * dev, u_long steps)
{
	TimerDef timer;

	DBG(_DBG_INFO, "u12motor_PositionYProc()\n");
	u12io_StartTimer(&timer, _SECOND * 5);

	u12io_ResetFifoLen();
	while (!(u12io_GetScanState(dev) & _SCANSTATE_STOP) &&
	       (!u12io_CheckTimer(&timer)));
	_DODELAY(12);
	u12motor_ModuleFreeRun(dev, steps);
	_DODELAY(15);

	u12io_StartTimer(&timer, _SECOND * 30);
	do {
		if (!(u12io_GetExtendedStatus(dev) & _STILL_FREE_RUNNING)) {
			break;
		}

		if (u12io_IsEscPressed()) {
			DBG(_DBG_INFO, "* CANCEL detected!\n");
			return SANE_STATUS_CANCELLED;
		}

	} while (!u12io_CheckTimer(&timer));
	DBG(_DBG_INFO, "u12motor_PositionYProc() - done\n");
	return SANE_STATUS_GOOD;
}

/** initialize this module and setup the correct function pointer according
 * to the ASIC
 */
static void
u12motor_PositionModuleToHome(U12_Device * dev)
{
	SANE_Byte rb[50];
	SANE_Byte save, saveModel;
	int c = 0;

	DBG(_DBG_INFO, "u12motor_PositionModuleToHome()\n");
	saveModel = dev->regs.RD_ModelControl;

	dev->scan.refreshState = SANE_FALSE;
	u12motor_DownloadNullScanStates(dev);

	_DODELAY(125);
	save = dev->shade.intermediate;

	dev->shade.intermediate = _ScanMode_AverageOut;
	u12hw_InitAsic(dev, SANE_FALSE);
	dev->shade.intermediate = save;

	_SET_REG(rb, c, REG_MODECONTROL, _ModeScan);
	_SET_REG(rb, c, REG_RESETMTSC, 0);
	_SET_REG(rb, c, REG_SCANCONTROL1, 0);
	_SET_REG(rb, c, REG_MODELCONTROL, (dev->ModelCtrl | _MODEL_DPI300));
	_SET_REG(rb, c, REG_LINECONTROL, 80);
	_SET_REG(rb, c, REG_XSTEPTIME, dev->XStepBack);
	_SET_REG(rb, c, REG_MOTORDRVTYPE, dev->MotorPower);
	_SET_REG(rb, c, REG_MOTOR0CONTROL, (_MotorHHomeStop | _MotorOn |
					    _MotorHQuarterStep |
					    _MotorPowerEnable));
	_SET_REG(rb, c, REG_STEPCONTROL,
		 (_MOTOR0_SCANSTATE | _MOTOR_FREERUN));
	u12io_DataToRegs(dev, rb, c);

	memset(dev->scanStates, 0x88, _SCANSTATE_BYTES);
	u12io_DownloadScanStates(dev);

	u12io_RegisterToScanner(dev, REG_REFRESHSCANSTATE);
	dev->regs.RD_ModelControl = saveModel;
}

/** function to bring the sensor back home
 */
static void
u12motor_ToHomePosition(U12_Device * dev, SANE_Bool wait)
{
	TimerDef timer;

	DBG(_DBG_INFO, "Waiting for Sensor to be back in position\n");
	if (!(u12io_DataFromRegister(dev, REG_STATUS) & _FLAG_PAPER)) {

		u12motor_PositionModuleToHome(dev);

		if (wait) {
			u12io_StartTimer(&timer, _SECOND * 20);
			do {
				if (u12io_DataFromRegister(dev, REG_STATUS) &
				    _FLAG_PAPER)
					break;
			} while (!u12io_CheckTimer(&timer));
		}
	}
	DBG(_DBG_INFO, "- done !\n");
}

/**
 */
static SANE_Status
u12motor_BackToHomeSensor(U12_Device * dev)
{
	SANE_Byte rb[20];
	int c;
	TimerDef timer;

	DBG(_DBG_INFO, "u12Motor_BackToHomeSensor()\n");

	c = 0;
	_SET_REG(rb, c, REG_STEPCONTROL, _MOTOR0_SCANSTATE);
	_SET_REG(rb, c, REG_MODECONTROL, _ModeScan);

	u12io_DataToRegs(dev, rb, c);

	u12motor_Force16Steps(dev, _DIR_NONE);

	/* stepping every state */
	memset(dev->scanStates, 0x88, _SCANSTATE_BYTES);
	u12io_DownloadScanStates(dev);
	_DODELAY(50);

	u12io_StartTimer(&timer, _SECOND * 2);

	u12io_ResetFifoLen();
	while (!(u12io_GetScanState(dev) & _SCANSTATE_STOP) &&
	       !u12io_CheckTimer(&timer)) {
		if (u12io_IsEscPressed()) {
			DBG(_DBG_INFO, "* CANCEL detected!\n");
			return SANE_STATUS_CANCELLED;
		}
	}

	u12motor_Force16Steps(dev, _DIR_BW);
	dev->regs.RD_ModeControl = _ModeScan;

	c = 0;

	if (!(dev->DataInf.dwScanFlag & _SCANDEF_TPA)) {
		_SET_REG(rb, c, REG_LINECONTROL,
			 _LOBYTE(dev->shade.wExposure));
		_SET_REG(rb, c, REG_XSTEPTIME, _LOBYTE(dev->shade.wXStep));
	} else {
		_SET_REG(rb, c, REG_LINECONTROL, _DEFAULT_LINESCANTIME);
		_SET_REG(rb, c, REG_XSTEPTIME, 6);
	}

	_SET_REG(rb, c, REG_STEPCONTROL,
		 (_MOTOR_FREERUN | _MOTOR0_SCANSTATE));
	_SET_REG(rb, c, REG_MOTOR0CONTROL,
		 (_MotorHQuarterStep | _MotorOn | _MotorDirBackward |
		  _MotorPowerEnable | _MotorHHomeStop));
	_SET_REG(rb, c, REG_REFRESHSCANSTATE, 0);
	u12io_DataToRegs(dev, rb, c);

	u12io_StartTimer(&timer, _SECOND * 5);
	do {
		if (u12io_DataFromRegister(dev, REG_STATUS) & _FLAG_PAPER)
			break;

		if (u12io_IsEscPressed()) {
			DBG(_DBG_INFO, "* CANCEL detected!\n");
			return SANE_STATUS_CANCELLED;
		}

		_DODELAY(55);

	} while (!u12io_CheckTimer(&timer));

	c = 0;
	_SET_REG(rb, c, REG_LINECONTROL, dev->regs.RD_LineControl);
	_SET_REG(rb, c, REG_XSTEPTIME, dev->regs.RD_XStepTime);
	u12io_DataToRegs(dev, rb, c);

	DBG(_DBG_INFO, "* LineCtrl=0x%02x, XStepTime=0x%02x\n",
	    dev->regs.RD_LineControl, dev->regs.RD_XStepTime);

	u12motor_DownloadNullScanStates(dev);
	return SANE_STATUS_GOOD;
}

/**
 */
static SANE_Status
u12motor_ModuleToHome(U12_Device * dev)
{
	SANE_Status res;

	DBG(_DBG_INFO, "u12motor_ModuleToHome()\n");
	if (!(u12io_DataFromRegister(dev, REG_STATUS) & _FLAG_PAPER)) {

		u12io_DataToRegister(dev, REG_MOTOR0CONTROL,
				     (SANE_Byte) (dev->regs.
						  RD_Motor0Control |
						  _MotorDirForward));

		res = u12motor_PositionYProc(dev, 40);
		if (SANE_STATUS_GOOD != res)
			return res;

		res = u12motor_BackToHomeSensor(dev);
		if (SANE_STATUS_GOOD != res)
			return res;

		_DODELAY(250);
	}
	DBG(_DBG_INFO, "* done.\n");
	return SANE_STATUS_GOOD;
}

/**
 */
static SANE_Status
u12motor_WaitForPositionY(U12_Device * dev)
{
	SANE_Byte rb[20];
	SANE_Status res;
	SANE_Byte bXStep;
	int c;
	u_long dwBeginY;

	c = 0;
	dwBeginY =
		(u_long) dev->DataInf.crImage.y * 4 + dev->scan.dwScanOrigin;

	if (dev->DataInf.wPhyDataType <= COLOR_256GRAY) {
		if (dev->f0_8_16)
			dwBeginY += 16;
		else
			dwBeginY += 8;
	}

	bXStep = (SANE_Byte) ((dev->DataInf.wPhyDataType <= COLOR_256GRAY) ?
			      dev->XStepMono : dev->XStepColor);

	if (dev->shade.intermediate & _ScanMode_AverageOut)
		bXStep = 8;

	u12motor_Force16Steps(dev, _DIR_NONE);
	dwBeginY -= 16;

	if (dwBeginY > (_RFT_SCANNING_ORG + _YOFFSET) &&
	    bXStep < dev->regs.RD_XStepTime) {

		u12io_DataToRegister(dev, REG_MOTORDRVTYPE, dev->MotorPower);
		_DODELAY(12);
		u12io_DataToRegister(dev, REG_XSTEPTIME, bXStep);
		u12io_DataToRegister(dev, REG_EXTENDEDXSTEP, 0);
		u12io_DataToRegister(dev, REG_SCANCONTROL1,
				     (SANE_Byte) (dev->regs.
						  RD_ScanControl1 &
						  ~_MFRC_RUNSCANSTATE));
		res = u12motor_PositionYProc(dev, dwBeginY - 64);
		if (SANE_STATUS_GOOD != res)
			return res;
		dwBeginY = 64;
	} else {
		_SET_REG(rb, c, REG_SCANCONTROL1, dev->regs.RD_ScanControl1);
	}

	_SET_REG(rb, c, REG_FIFOFULLEN0, _LOBYTE(dev->regs.RD_BufFullSize));
	_SET_REG(rb, c, REG_FIFOFULLEN1, _HIBYTE(dev->regs.RD_BufFullSize));
	_SET_REG(rb, c, REG_FIFOFULLEN2,
		 _LOBYTE(_HIWORD(dev->regs.RD_BufFullSize)));
	u12io_DataToRegs(dev, rb, c);

	u12io_DataToRegister(dev, REG_MOTORDRVTYPE,
			     dev->regs.RD_MotorDriverType);
	_DODELAY(12);

	if (!dev->f2003 || (dev->shade.intermediate & _ScanMode_AverageOut) ||
	    (dev->DataInf.xyAppDpi.y <= 75 &&
	     dev->DataInf.wPhyDataType <= COLOR_256GRAY)) {
		u12io_DataToRegister(dev, REG_MOTORDRVTYPE,
				     (SANE_Byte) (dev->
						  MotorPower & (_MOTORR_MASK |
								_MOTORR_STRONG)));
	} else {
		u12io_DataToRegister(dev, REG_MOTORDRVTYPE,
				     dev->regs.RD_MotorDriverType);
	}

	c = 0;
	_SET_REG(rb, c, REG_XSTEPTIME, dev->regs.RD_XStepTime);
	_SET_REG(rb, c, REG_EXTENDEDXSTEP, dev->regs.RD_ExtXStepTime);
	_SET_REG(rb, c, REG_SCANCONTROL1,
		 (SANE_Byte) (dev->regs.
			      RD_ScanControl1 & ~_MFRC_RUNSCANSTATE));
	u12io_DataToRegs(dev, rb, c);

	if (dev->DataInf.dwScanFlag & _SCANDEF_PREVIEW) {

		TimerDef timer;

		u12motor_ModuleFreeRun(dev, dwBeginY);
		_DODELAY(15);

		u12io_StartTimer(&timer, (_SECOND * 20));

		while ((u12io_GetExtendedStatus(dev) & _STILL_FREE_RUNNING) &&
		       !u12io_CheckTimer(&timer));
		u12io_DataToRegister(dev, REG_MODECONTROL, _ModeScan);
	} else {
		u12motor_PositionYProc(dev, dwBeginY);
		u12io_RegisterToScanner(dev, REG_REFRESHSCANSTATE);
	}
	return SANE_STATUS_GOOD;
}

/**
 */
static void
u12motor_ForceToLeaveHomePos(U12_Device * dev)
{
	SANE_Byte rb[4];
	TimerDef timer;

	DBG(_DBG_INFO, "u12motor_ForceToLeaveHomePos()\n");
	rb[0] = REG_STEPCONTROL;
	rb[1] = _MOTOR0_ONESTEP;
	rb[2] = REG_MOTOR0CONTROL;
	rb[3] = _FORWARD_MOTOR;
	u12io_DataToRegs(dev, rb, 2);

	u12io_StartTimer(&timer, _SECOND);

	do {
		if (!(u12io_DataFromRegister(dev, REG_STATUS) & _FLAG_PAPER))
			break;

		u12io_RegisterToScanner(dev, REG_FORCESTEP);
		_DODELAY(10);

	} while (!u12io_CheckTimer(&timer));

	u12io_DataToRegister(dev, REG_STEPCONTROL, _MOTOR0_SCANSTATE);
}

/** move the sensor to the appropriate shading position
 */
static SANE_Status
u12motor_GotoShadingPosition(U12_Device * dev)
{
	SANE_Byte rb[20];
	SANE_Status res;
	int c;

	DBG(_DBG_INFO, "u12motor_GotoShadingPosition()\n");
	res = u12motor_ModuleToHome(dev);
	if (SANE_STATUS_GOOD == res)
		return res;

	/* position to somewhere under the transparency adapter */
	if (dev->DataInf.dwScanFlag & _SCANDEF_TPA) {

		u12motor_ForceToLeaveHomePos(dev);
		u12motor_DownloadNullScanStates(dev);

		c = 0;
		_SET_REG(rb, c, REG_STEPCONTROL, _MOTOR0_SCANSTATE);
		_SET_REG(rb, c, REG_MODECONTROL, _ModeScan);
		_SET_REG(rb, c, REG_MOTOR0CONTROL, _FORWARD_MOTOR);
		_SET_REG(rb, c, REG_XSTEPTIME, 6);
		_SET_REG(rb, c, REG_EXTENDEDXSTEP, 0);
		_SET_REG(rb, c, REG_SCANCONTROL1, _MFRC_BY_XSTEP);
		u12io_DataToRegs(dev, rb, c);

		res = u12motor_PositionYProc(dev, _TPA_SHADINGORG);
		if (SANE_STATUS_GOOD != res)
			return res;
	}
	DBG(_DBG_INFO, "* Position reached\n");
	return SANE_STATUS_GOOD;
}

/**
 */
static void
u12motor_ModuleForwardBackward(U12_Device * dev)
{
	DBG(_DBG_INFO, "u12motor_ModuleForwardBackward()\n");

	switch (dev->scan.bModuleState) {

	case _MotorInNormalState:
		DBG(_DBG_INFO, "* _MotorInNormalState\n");
		dev->scan.bModuleState = _MotorGoBackward;
		u12io_DataToRegister(dev, REG_SCANCONTROL1,
				     (SANE_Byte) (dev->regs.
						  RD_ScanControl1 &
						  ~_MFRC_RUNSCANSTATE));
		u12io_DataToRegister(dev, REG_MOTOR0CONTROL,
				     (SANE_Byte) (dev->regs.
						  RD_Motor0Control &
						  ~_MotorDirForward));

		u12motor_ModuleFreeRun(dev, _BACKSTEPS);
		u12io_StartTimer(&u12motor_Timer, (15 * _MSECOND));
		break;

	case _MotorGoBackward:
		DBG(_DBG_INFO, "* _MotorGoBackward\n");
		if (u12io_CheckTimer(&u12motor_Timer)) {
			if (!
			    (u12io_GetExtendedStatus(dev) &
			     _STILL_FREE_RUNNING)) {
				dev->scan.bModuleState = _MotorInStopState;
				u12io_StartTimer(&u12motor_Timer,
						 (50 * _MSECOND));
			}
		}
		break;

	case _MotorInStopState:
		DBG(_DBG_INFO, "* _MotorInStopState\n");
		if (u12io_CheckTimer(&u12motor_Timer)) {

			if (u12io_GetFifoLength(dev) <
			    dev->scan.dwMaxReadFifo) {
				dev->scan.bModuleState = _MotorAdvancing;
				u12io_DataToRegister(dev, REG_SCANCONTROL1,
						     dev->regs.
						     RD_ScanControl1);
				u12io_DataToRegister(dev, REG_MOTOR0CONTROL,
						     dev->regs.
						     RD_Motor0Control);
				u12motor_ModuleFreeRun(dev, _FORWARDSTEPS);
				u12io_StartTimer(&u12motor_Timer,
						 (15 * _MSECOND));
			}
		}
		break;

	case _MotorAdvancing:
		DBG(_DBG_INFO, "* _MotorAdvancing\n");
		if (u12io_CheckTimer(&u12motor_Timer)) {
			if (!(u12io_GetScanState(dev) & _SCANSTATE_STOP))
				dev->scan.bModuleState = _MotorInNormalState;
			else {
				if (!
				    (u12io_GetExtendedStatus(dev) &
				     _STILL_FREE_RUNNING)) {
					u12io_RegisterToScanner(dev,
								REG_REFRESHSCANSTATE);
					dev->scan.bModuleState =
						_MotorInNormalState;
				}
			}
		}
		break;
	}
}

/* END U12-MOTOR.C ..........................................................*/
